%{
#include "treefuncs.h"
#include "structs.h"
#include <stdio.h>
#include <malloc.h>


extern int yylex(void);

struct Program *prg;
%}
%union {
	int Int;
    float Float;
    char *Id;
    char *Variable;
    char *String_c;			
    
    
    struct Program *prg;
    
    struct NExpr *Expr;
    struct NExprList *ExprList;
    
    struct StmtList *stmtList;
    struct Stmt *_stmt;
    
    struct FuncParamList *_funcParamList;
    struct FuncParam *_funcParam;
}

%type <prg> program

%type <Expr> expr
%type <ExprList> expr_list
%type <ExprList> expr_list_ne


%type <stmtList> top_stmt_list
%type <stmtList> top_stmt_list_ne
%type <stmtList> inner_stmt_list
%type <stmtList> inner_stmt_list_ne
%type <stmtList> class_decl_stmt_list
%type <stmtList> class_decl_stmt_list_ne
%type <_stmt> top_stmt
%type <_stmt> inner_stmt
%type <_stmt> print_stmt
%type <_stmt> if_stmt
%type <_stmt> while_stmt
%type <_stmt> for_stmt
%type <_stmt> foreach_stmt
%type <_stmt> class_decl_stmt
%type <_stmt> func_decl_stmt
%type <_stmt> field_decl_stmt
%type <_stmt> method_decl_stmt
%type <_stmt> class_decl_stmt_list_item
%type <_funcParamList> func_param_list_ne
%type <_funcParamList> func_param_list
%type <_funcParam> func_param;

%token <Int> INT
%token <Float> FLOAT
%token <Id> ID
%token <Variable> VARIABLE
%token <String_c> STRINGCONST

%token AS
%token CLASS
%token ECHO
%token ELSE
%token EXTENDS
%token FOR
%token FOREACH
%token FUNCTION
%token IF
%token PRINT
%token PRIVATE
%token PROTECTED
%token PUBLIC
%token RETURN
%token STATIC
%token WHILE
%token ARROW
%token KEYVALUE


%right '='
%left OROPER
%left ANDOPER
%nonassoc EQUAL NOTEQUAL
%nonassoc '>' '<'
%left '-' '+' CONCATENATION
%left '*' '/' '%'
%nonassoc '!' UMINUS UPLUS
%left '[' ARROW DOUBLECOLON
%nonassoc ')'

%start program

%%
program:  top_stmt_list {$$ = prg = createProgram($1);}
       ;
top_stmt_list: /*empty*/ {$$=NULL;}
			 | top_stmt_list_ne {$$=$1;}
			 ;
			 
top_stmt_list_ne: top_stmt {$$=CreateStmtList($1);}
                | top_stmt_list_ne top_stmt	 {$$=AddToStmtList($1, $2);}		 
		        ;
				
inner_stmt_list: /*empty*/ {$$=NULL} 
			   | inner_stmt_list_ne {$$=$1}
			   ;
			   
inner_stmt_list_ne: inner_stmt {$$=CreateStmtList($1);}
				  | inner_stmt_list_ne inner_stmt {$$=AddToStmtList($1, $2);}
				  ;
				  
top_stmt: inner_stmt {$$=$1;}
		| func_decl_stmt {$$=$1;}
		| class_decl_stmt {$$=$1;}
		;
		
inner_stmt: expr ';' {$$=CreateExprStmt($1);}
	| '{' inner_stmt_list '}' {$$=CreateStmtListStmt($2);}
    | print_stmt ';' {$$=$1}
	| if_stmt  {$$=$1}
	| while_stmt  {$$=$1}
	| for_stmt  {$$=$1}
	| foreach_stmt {$$=$1}
	| RETURN ';' {$$=CreateReturnStmt(NULL);}
	| RETURN expr ';' {$$=CreateReturnStmt($2);}
    ; 		

print_stmt: ECHO expr_list {$$ = CreatePrintStmt(Echo, $2);}
          | PRINT  expr_list {$$ = CreatePrintStmt(Print, $2);}
          ;	
		  
if_stmt: IF '(' expr ')' inner_stmt {$$=CreateIfStmt($3, $5, NULL);}
	   | IF '(' expr ')' inner_stmt ELSE inner_stmt {$$=CreateIfStmt($3,$5, $7);}
	   ;
	   
while_stmt: WHILE '(' expr ')' inner_stmt {$$=CreateWhileStmt($3, $5);}
          ;
		  
for_stmt: FOR '(' expr ';' expr ';' expr ')' inner_stmt {$$=CreateForStmt($3, $5, $7, $9);}
        ;	
		
foreach_stmt: FOREACH '(' expr AS VARIABLE KEYVALUE VARIABLE ')' inner_stmt {$$=CreateForeachStmt($3, $5, $7, $9);}
			| FOREACH '(' expr AS VARIABLE ')' inner_stmt {$$=CreateForeachStmt($3, $5, NULL, $7);}
            ;
			
func_param_list: /*empty*/ {$$=NULL}
               | func_param_list_ne {$$=$1}
			   ;
func_param: VARIABLE {$$=CreateFuncParam($1, NULL);}
 		  | VARIABLE '=' expr {$$=CreateFuncParam($1, $3);}       	   
		  
func_param_list_ne: func_param {$$=CreateFuncParamList($1);}
                  | func_param_list ',' func_param {$$ = AddToFuncParamList($1, $3);}
				  ;				   
				  
func_decl_stmt: FUNCTION ID '(' func_param_list ')' '{' inner_stmt_list '}' {$$=CreateFuncDeclStmt($2, $4, $7);}
			 ;
		   
class_decl_stmt: CLASS ID EXTENDS ID '{' class_decl_stmt_list '}' {$$=CreateClassDeclStmt($2, $4, $6);}
               | CLASS ID '{' class_decl_stmt_list '}' {$$=CreateClassDeclStmt($2, NULL, $4);}
			   ;
			   
class_decl_stmt_list: /*empty*/ {$$=NULL}
                    | class_decl_stmt_list_ne {$$=$1}
					;
					
class_decl_stmt_list_ne: class_decl_stmt_list_item {$$=CreateStmtList($1);}
					   | class_decl_stmt_list_ne class_decl_stmt_list_item {$$=AddToStmtList($1, $2);}
					   ;
					   
class_decl_stmt_list_item: field_decl_stmt {$$=$1}
						 | method_decl_stmt {$$=$1}
						 ;
	
field_decl_stmt: STATIC PUBLIC VARIABLE '=' expr ';' {$$=CreateFieldDeclStmt(1, Public, $3, $5);}
			   | STATIC PRIVATE VARIABLE '=' expr ';' {$$=CreateFieldDeclStmt(1, Private, $3, $5);}
			   | STATIC PROTECTED VARIABLE '=' expr ';' {$$=CreateFieldDeclStmt(1, Protected, $3, $5);}
               | STATIC VARIABLE '=' expr ';' {$$=CreateFieldDeclStmt(1, Public, $2, $4);}
			   | STATIC PUBLIC VARIABLE ';' {$$=CreateFieldDeclStmt(1, Public, $3, NULL);}
			   | STATIC PRIVATE VARIABLE ';' {$$=CreateFieldDeclStmt(1, Private, $3, NULL);}
			   | STATIC PROTECTED VARIABLE ';' {$$=CreateFieldDeclStmt(1, Protected, $3, NULL);}
               | STATIC VARIABLE ';' {$$=CreateFieldDeclStmt(1, Private, $2, NULL);}
			   | PUBLIC VARIABLE ';' {$$=CreateFieldDeclStmt(0, Public, $2, NULL);}
			   | PRIVATE VARIABLE ';' {$$=CreateFieldDeclStmt(0, Private, $2, NULL);}
			   | PROTECTED VARIABLE ';' {$$=CreateFieldDeclStmt(0, Protected, $2, NULL);}
               | VARIABLE ';' {$$=CreateFieldDeclStmt(0, Private, $1, NULL);}
			   ;
			   
method_decl_stmt: STATIC PUBLIC func_decl_stmt {$$=CreateMethodDeclStmt(1, Public, $3);}
				| STATIC PROTECTED func_decl_stmt {$$=CreateMethodDeclStmt(1, Protected, $3);}
				| STATIC PRIVATE func_decl_stmt {$$=CreateMethodDeclStmt(1, Private, $3);}
				| PUBLIC func_decl_stmt {$$=CreateMethodDeclStmt(0, Public, $2);}
				| PRIVATE func_decl_stmt {$$=CreateMethodDeclStmt(0, Protected, $2);} 
				| PROTECTED func_decl_stmt {$$=CreateMethodDeclStmt(0, Private, $2);}
				| func_decl_stmt {$$=CreateMethodDeclStmt(0, Private, $1);}
				| STATIC func_decl_stmt {$$=CreateMethodDeclStmt(1, Private, $2);}
				;
		 
expr: ID {$$ = createExpr(Id);}
    | INT {$$ = createExpr(Int);}
    | FLOAT {$$ = createExpr(Float);}
    | VARIABLE {$$ = createExpr(Variable);}
    | STRINGCONST {$$ = createExpr(StringConst);}
    | expr '-' expr {$$ = createBinaryExpr(Minus, $1, $3);}
    | expr '+' expr {$$ = createBinaryExpr(Plus, $1, $3);}
    | expr '*' expr {$$ = createBinaryExpr(Mul, $1, $3);}
    | expr '/' expr {$$ = createBinaryExpr(Div, $1, $3);}
    | expr '>' expr {$$ = createBinaryExpr(More, $1, $3);}
    | expr '<' expr {$$ = createBinaryExpr(Less, $1, $3);}
    | expr '=' expr {$$ = createBinaryExpr(Assign, $1, $3);}
    | expr CONCATENATION expr {$$ = createBinaryExpr(Concatenation, $1, $3);}
    | expr OROPER expr {$$ = createBinaryExpr(OrOper, $1, $3);}
    | expr ANDOPER expr {$$ = createBinaryExpr(AndOper, $1, $3);}
    | expr EQUAL expr {$$ = createBinaryExpr(Equal, $1, $3);}
    | expr NOTEQUAL expr {$$ = createBinaryExpr(NotEqual, $1, $3);}
    | '+' expr %prec UPLUS {$$ = createUnaryExpr(UPlus, $2);}
	| '-' expr %prec UMINUS {$$ = createUnaryExpr(UMinus, $2);}
    | '!' expr {$$ = createUnaryExpr(Not, $2);}
	| ID '(' expr_list ')' {$$ = CreateCallMethodExpr(CallFunc,NULL, $3, $1);}
	| '(' expr ')'	 {$$ = $2;}
    | expr '[' expr ']' {$$ = createIndArrayExpr($1, $3);}
	| expr '[' ']' {$$ = createEmptyIndArrayExpr($1);}
    | expr ARROW ID '(' expr_list ')' {$$ = CreateCallMethodExpr(Arrow,$1, $5, $3);}
    | expr ARROW ID  {$$ = CreateCallMethodExpr(Arrow,$1, NULL, $3);}
    | ID DOUBLECOLON expr {$$ = createDoubleColonExpr($1, $3);}
    ;
	
expr_list: /*empty*/ {$$=NULL;}
         | expr_list_ne {$$=$1;}
		 ;
		 
expr_list_ne: expr_list_ne ',' expr {$$ = addToExprList($1,$3);}
		 | expr {$$ = createExprList($1);}
		 ;
				
%%
int yyerror(char const *str)
{
    printf("error occured");
};
		  